using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Globalization;
using System.Configuration;
using Barbar.MapReport.PolygonReducer.Configuration;

namespace Barbar.MapReport.PolygonReducer {
  internal class Program {
    static void Main() {
      Graph graph = new Graph();
      QuerySelect query = PolygonReducerConfig.Instance.QuerySelect;
      using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["PolygonReducer"].ConnectionString)) {
        conn.Open();
        SqlCommand cmd = null;
        cmd = new SqlCommand(query.QueryCount, conn);
        long recordsCountTotal = Convert.ToInt64(cmd.ExecuteScalar(), CultureInfo.InvariantCulture);
        long percentCount = recordsCountTotal / 100;
        long recordsCount = 0;
        cmd = new SqlCommand(query.Query, conn);
        string lastPolygonId = String.Empty;
        int lastPolylineIndex = -1;
        Node lastPoint = null;
        Node firstPoint = null;
        using (SqlDataReader reader = cmd.ExecuteReader()) {
          while (reader.Read()) {
            string regionId = reader.GetString(reader.GetOrdinal(query.RegionId));
            long polylineIndex = reader.GetInt64(reader.GetOrdinal(query.PolylineIndex));
            long index = reader.GetInt64(reader.GetOrdinal(query.Index));
            decimal latitude = reader.GetDecimal(reader.GetOrdinal(query.Latitude));
            decimal longitude = reader.GetDecimal(reader.GetOrdinal(query.Longitude));

            recordsCount++;
            if (recordsCount % percentCount == 0)
              Console.Out.WriteLine("Reading data {0} % finished.", recordsCount / percentCount);

            Node node = new Node(latitude, longitude, 0.0m);
            PointRef pointRef = new PointRef(regionId, (int)polylineIndex, (int)index);
            Node storedPoint = graph[node];
            if (storedPoint == null) {
              graph.AddPoint(node);
              storedPoint = node;
            }

            storedPoint.Points.Add(pointRef);

            if ((lastPolygonId != regionId && lastPolygonId != String.Empty) ||
              (lastPolylineIndex != polylineIndex && lastPolylineIndex != -1)) {
              firstPoint.AddAdjectNode(lastPoint);
              lastPoint = null;
            }

            if (lastPoint != null)
              storedPoint.AddAdjectNode(lastPoint);
            else
              firstPoint = storedPoint;

            lastPoint = storedPoint;
            lastPolygonId = regionId;
            lastPolylineIndex = (int)polylineIndex;
          }
          firstPoint.AddAdjectNode(lastPoint);
          reader.Close();
        }

        List<Node> node3 = new List<Node>(graph.GetNodesWithAtLeast3AdjectNodes());
        graph.RemoveRange(node3);
        List<List<Node>> polyLines = new List<List<Node>>();
        foreach (Node node in node3) {
          foreach (Node adjectNode in node.AdjectNodes) {
            if (graph[adjectNode] != null) {
              graph.RemovePoint(adjectNode);
              List<Node> polyLine = new List<Node>(new Node[] { node, adjectNode });
              Node currentNode = adjectNode;
              bool iterate = true;
              while (iterate) {
                iterate = false;
                foreach (Node child in currentNode.AdjectNodes) {
                  if (child == node)
                    continue;
                  if (child.AdjectNodes.Count > 2) {
                    polyLine.Add(child);
                    break;
                  }
                  if (graph[child] != null) {
                    iterate = true;
                    polyLine.Add(child);
                    graph.RemovePoint(child);
                    currentNode = child;
                    break;
                  }
                }
              }
              int count = polyLine.Count;
              polyLines.Add(polyLine);
            }
          }
        }

        cmd = new SqlCommand(PolygonReducerConfig.Instance.DeleteReducedPointsQuery, conn);
        cmd.ExecuteNonQuery();


        foreach (List<Node> polyLine in polyLines) {
          DouglasPeucker dp = new DouglasPeucker();
          List<Node> simplifiedPolyLine = dp.SimplifyPolyline(0.5f, polyLine);
          foreach (Node point in simplifiedPolyLine) {
            foreach (PointRef pointRef in point.Points) {
              cmd = new SqlCommand(PolygonReducerConfig.Instance.InsertReducedPointQuery, conn);
              cmd.Parameters.Add("@RegionId", SqlDbType.NVarChar, 7).Value = pointRef.PolygonId;
              cmd.Parameters.Add("@PolylineIndex", SqlDbType.Int).Value = pointRef.PolylineIndex;
              cmd.Parameters.Add("@Index", SqlDbType.BigInt).Value = pointRef.Index;
              cmd.Parameters.Add("@Latitude", SqlDbType.Decimal).Value = point.X;
              cmd.Parameters.Add("@Longitude", SqlDbType.Decimal).Value = point.Y;
              cmd.ExecuteNonQuery();
            }
          }
        }

        foreach (Node point in node3) {
          foreach (PointRef pointRef in point.Points) {
            cmd = new SqlCommand(PolygonReducerConfig.Instance.InsertReducedPointQuery, conn);
            cmd.Parameters.Add("@RegionId", SqlDbType.NVarChar, 7).Value = pointRef.PolygonId;
            cmd.Parameters.Add("@PolylineIndex", SqlDbType.Int).Value = pointRef.PolylineIndex;
            cmd.Parameters.Add("@Index", SqlDbType.BigInt).Value = pointRef.Index;
            cmd.Parameters.Add("@Latitude", SqlDbType.Decimal).Value = point.X;
            cmd.Parameters.Add("@Longitude", SqlDbType.Decimal).Value = point.Y;
            cmd.ExecuteNonQuery();
          }
        }
        conn.Close();
      }
    }
  }
}



